package com.r2coding.server.modules.user.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.r2coding.cache.core.constants.CacheConstants;
import com.r2coding.core.exception.Pan4jException;
import com.r2coding.core.response.ResponseCode;
import com.r2coding.core.utils.IdUtil;
import com.r2coding.core.utils.JwtUtil;
import com.r2coding.core.utils.PasswordUtil;
import com.r2coding.server.common.utils.UserIdUtil;
import com.r2coding.server.modules.file.constants.FileConstants;
import com.r2coding.server.modules.file.context.CreateFolderContext;
import com.r2coding.server.modules.file.entity.UserFile;
import com.r2coding.server.modules.file.enums.DelFlagEnum;
import com.r2coding.server.modules.file.enums.FolderFlagEnum;
import com.r2coding.server.modules.file.mapper.UserFileMapper;
import com.r2coding.server.modules.file.service.IUserFileService;
import com.r2coding.server.modules.user.constants.UserConstants;
import com.r2coding.server.modules.user.context.UserLoginContext;
import com.r2coding.server.modules.user.entity.User;
import com.r2coding.server.modules.user.mapper.UserMapper;
import com.r2coding.server.modules.user.po.*;
import com.r2coding.server.modules.user.service.IUserService;
import com.r2coding.server.modules.user.vo.UserInfoVo;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.Objects;

/**
 * @author 98k灬
 * @version v1.0.0
 * @Description : 功能描述
 * @Create on : 2023/9/24 09:46
 **/
@Service
public class IUserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
    @Autowired
    private IUserFileService userFileService;

    @Autowired
    private CacheManager cacheManager;

    @Autowired
    private UserFileMapper userFileMapper;

    /**
     * 注册
     *
     * @param userRegisterPo 注册信息
     * @return 用户id
     */
    @Override
    public Long register(UserRegisterPo userRegisterPo) {
        // 检查注册信息
        this.checkRegisterInfo(userRegisterPo);
        // 注册用户信息
        Long userId = this.doRegister(userRegisterPo);
        // 创建文件夹
        this.createUserRootFolder(userId);
        return userId;
    }

    /**
     * 登录
     *
     * @param userLoginPo 登录信息
     * @return 访问token
     */
    @Override
    public String login(UserLoginPo userLoginPo) {
        UserLoginContext userLoginContext = new UserLoginContext();
        BeanUtils.copyProperties(userLoginPo, userLoginContext);
        // 检查登录信息
        this.checkLoginInfo(userLoginContext);
        // 生成并保存访问token
        this.createAndSaveToken(userLoginContext);
        return userLoginContext.getToken();
    }

    /**
     * 登出
     *
     * @param userId 用户id
     */
    @Override
    public void exit(Long userId) {
        try {
            Cache cache = cacheManager.getCache(CacheConstants.CACHE_NAME);
            cache.evict(UserConstants.USER_LOGIN_PREFIX + userId);
        } catch (Exception exception) {
            exception.printStackTrace();
            throw new Pan4jException("用户退出登录失败");
        }
    }

    /**
     * 用户名称检查
     *
     * @param checkUserNamePo 请求信息
     */
    @Override
    public String checkUsername(CheckUserNamePo checkUserNamePo) {
        User user = this.getUserByUsername(checkUserNamePo.getUsername());
        if (Objects.isNull(user)) {
            throw new Pan4jException("用户名称不存在");
        }
        return user.getQuestion();
    }

    /**
     * 校验密保答案
     *
     * @param checkAnswerPo 请求信息
     * @return token
     */
    @Override
    public String checkAnswer(CheckAnswerPo checkAnswerPo) {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(User::getUsername, checkAnswerPo.getUsername())
                .eq(User::getAnswer, checkAnswerPo.getAnswer())
                .eq(User::getQuestion, checkAnswerPo.getQuestion());
        User user = this.baseMapper.selectOne(wrapper);
        if (Objects.isNull(user)) {
            throw new Pan4jException("参数非法");
        }
        return JwtUtil.generateToken(checkAnswerPo.getUsername(), UserConstants.FORGET_USERNAME, checkAnswerPo.getUsername(), UserConstants.FIVE_MINUTE_LONG);
    }

    /**
     * 重置密码
     *
     * @param resetPasswordPo 请求信息
     */
    @Override
    public void passwordReset(ResetPasswordPo resetPasswordPo) {
        String password = resetPasswordPo.getPassword();
        String username = resetPasswordPo.getUsername();
        String token = resetPasswordPo.getToken();

        Object value = JwtUtil.analyzeToken(token, UserConstants.FORGET_USERNAME);
        if (Objects.isNull(value)) {
            throw new Pan4jException(ResponseCode.TOKEN_EXPIRE);
        }

        String tokenUsername = String.valueOf(value);
        if (!StringUtils.equals(tokenUsername, username)) {
          throw new Pan4jException("token错误");
        }

        User user = this.getUserByUsername(username);
        if (Objects.isNull(user)) {
            throw new Pan4jException("用户名称不存在");
        }

        String newPwd = PasswordUtil.encryptPassword(user.getSalt(), password);
        user.setPassword(newPwd).setUpdateTime(new Date());
        this.baseMapper.updateById(user);
    }

    /**
     * 在线修改用户密码
     *
     * @param changePasswordPo 请求信息
     */
    @Override
    public void changePwd(ChangePasswordPo changePasswordPo) {
        Long userId = UserIdUtil.get();
        String oldPassword = changePasswordPo.getOldPassword();
        String newPassword = changePasswordPo.getNewPassword();
        // 校验旧密码
        User user = this.baseMapper.selectById(userId);
        if (Objects.isNull(user)) {
            throw new Pan4jException("用户不存在");
        }
        String encOldPwd = PasswordUtil.encryptPassword(user.getSalt(), oldPassword);
        if (!StringUtils.equals(user.getPassword(), encOldPwd)) {
            throw new Pan4jException("旧密码不正确");
        }
        // 重置新密码
        String encNewPwd = PasswordUtil.encryptPassword(user.getSalt(), newPassword);
        user.setPassword(encNewPwd).setUpdateTime(new Date());
        this.baseMapper.updateById(user);
        // 退出当前的登录状态
        this.exit(userId);
    }

    /**
     * 用户信息
     *
     * @return 用户信息
     */
    @Override
    public UserInfoVo info() {
        Long userId = UserIdUtil.get();
        User user = this.baseMapper.selectById(userId);
        if (Objects.isNull(user)) {
            throw new Pan4jException("用户不存在");
        }
        UserInfoVo userInfoVo = UserInfoVo.builder()
                .username(user.getUsername())
                .build();
        LambdaQueryWrapper<UserFile> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(UserFile::getUserId, userId)
                .eq(UserFile::getDelFlag, DelFlagEnum.NO.getCode())
                .eq(UserFile::getFolderFlag, FolderFlagEnum.YES.getCode())
                .eq(UserFile::getParentId, FileConstants.TOP_PARENT_ID);
        UserFile userFile = userFileMapper.selectOne(wrapper);
        if (Objects.isNull(userFile)) {
            throw new Pan4jException("用户未创建文件");
        }
        userInfoVo.setRootFileId(userFile.getFileId()).setRootFilename(userFile.getFilename());
        return userInfoVo;
    }

    private void createAndSaveToken(UserLoginContext userLoginContext) {
        User user = userLoginContext.getEntity();
        String token = JwtUtil.generateToken(userLoginContext.getUsername(),
                UserConstants.LOGIN_USER_ID, user.getUserId(), UserConstants.ONE_DAY_LONG);
        Cache cache = cacheManager.getCache(CacheConstants.CACHE_NAME);
        cache.put(UserConstants.USER_LOGIN_PREFIX + user.getUserId(), token);
        userLoginContext.setToken(token);
    }

    private void checkRegisterInfo(UserRegisterPo userRegisterPo) {
        User user = this.getUserByUsername(userRegisterPo.getUsername());
        if (Objects.nonNull(user)) {
            throw new Pan4jException("用户名称已存在");
        }
    }

    private void checkLoginInfo(UserLoginContext userLoginContext) {
        String username = userLoginContext.getUsername();
        String password = userLoginContext.getPassword();

        User user = this.getUserByUsername(username);
        if (Objects.isNull(user)) {
            throw new Pan4jException("用户名称不存在");
        }

        String salt = user.getSalt();
        String encPwd = PasswordUtil.encryptPassword(salt, password);
        String dbPwd = user.getPassword();
        if (!StringUtils.equals(encPwd, dbPwd)) {
            throw new Pan4jException("密码不正确");
        }
        userLoginContext.setEntity(user);
    }

    private User getUserByUsername(String username) {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(User::getUsername, username);
        return this.baseMapper.selectOne(wrapper);
    }

    private Long doRegister(UserRegisterPo userRegisterPo) {
        User user = new User();
        BeanUtils.copyProperties(userRegisterPo, user);
        Long userId = IdUtil.get();
        String salt = PasswordUtil.getSalt();
        Date currentDate = new Date();
        user.setUserId(userId)
                .setSalt(salt)
                .setPassword(PasswordUtil.encryptPassword(salt, userRegisterPo.getPassword()))
                .setUpdateTime(currentDate)
                .setCreateTime(currentDate);
        this.baseMapper.insert(user);
        return userId;
    }

    private void createUserRootFolder(Long userId) {
        CreateFolderContext folderContext = CreateFolderContext.builder()
                .parentId(FileConstants.TOP_PARENT_ID)
                .userId(userId)
                .folderName(FileConstants.ALL_FILE_CN_STR)
                .build();
        userFileService.createFolder(folderContext);
    }
}
